/**
* \file DGVLatticeVTK.cpp
*
* \brief This file defines a class for displaying lattices using VTK.
* \author Shekhar S. Chandra, 2008
*
* This file is part of DGV Library.
*
* DGV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DGV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DGV.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "DGVLatticeVTK.h"

#include <QFileInfo>

#include "vtkTransform.h"

DGVLatticeVTK::DGVLatticeVTK(QWidget *parent) : QVTKWidget(parent)
{
    unit = 10.0;
    defaultSize = 13;
    setDimensions(2);
    defaultRadius = 1.0;
    generatedBefore = false;
    rightHanded = true;
    axesEnabledBefore = false;

    property = vtkProperty::New();
}

DGVLatticeVTK::~DGVLatticeVTK()
{
    if(generatedBefore)
    {
        ///Cleanup
        sphere->Delete();
        mapper->Delete();
        if(dimension == 1 || dimension == 2 || dimension == 3)
        {
            for(int j = 0; j < latticePoints.size(); j ++)
                latticePoints(j)->Delete();
        }
        renderer->Delete();
        renderWin->Delete();
        if(axesEnabledBefore)
            axes->Delete();
    }

    property->Delete();
}

void DGVLatticeVTK::setName(QString filename)
{
    name = filename;
    setWindowTitle(strippedNamePrefix());
}

void DGVLatticeVTK::setDimensions(int dimensions)
{
    dimension = dimensions;
    latticeParameters.resize(dimension);
    latticeParameters = unit;
    latticeSize.resize(dimension);
    latticeSize = defaultSize;
}

void DGVLatticeVTK::setDefaultSize(int value)
{
    defaultSize = value;
    latticeSize = defaultSize;
}

void DGVLatticeVTK::setBackgroundColour(double red, double green, double blue)
{
    renderer->SetBackground(red/256,green/256,blue/256);
    renderWin->Render();
}

void DGVLatticeVTK::setLatticeColour(double red, double green, double blue)
{
    int count = 0;

    if(generatedBefore)
    {
        if(dimension == 2)
        {
            for(int j = 0; j < latticeSize(0); j ++)
            {
                for(int k = 0; k < latticeSize(1); k ++)
                {
                    latticePoints(count)->GetProperty()->SetColor(red,green,blue);
                    count ++;
                }
            }
        }
    }
    else
        property->SetColor(red,green,blue);
}

void DGVLatticeVTK::setLatticePointColour(int x, int y, double red, double green, double blue)
{
    if(generatedBefore)
    {
        if(dimension == 2)
            latticePoints((y+x*latticeSize(1))%latticePoints.size())->GetProperty()->SetColor(red,green,blue);
    }
}

void DGVLatticeVTK::viewAsRightHanded()
{
    if(!rightHanded)
    {
        vtkCamera *camera = renderer->GetActiveCamera();

        camera->Roll(90);

        rightHanded = true;
    }
}

void DGVLatticeVTK::viewAsMatrix()
{
    if(rightHanded)
    {
        vtkCamera *camera = renderer->GetActiveCamera();

        camera->Roll(-90);

        rightHanded = false;
    }
}

void DGVLatticeVTK::enableAxes()
{
    if(!axesEnabledBefore)
    {
        axes = vtkAxesActor::New();
        axesEnabledBefore = true;
    }

    //axes->SetTotalLength(latticeParameters(0)*latticeSize(0),latticeParameters(1)*latticeSize(1),0);
    axes->SetTotalLength(latticeParameters(0)*0.75,latticeParameters(1)*0.75,0);
    axes->AxisLabelsOff();
    renderer->AddActor(axes);
}

void DGVLatticeVTK::disableAxes()
{
    if(!axesEnabledBefore)
    {
        axes = vtkAxesActor::New();
        axesEnabledBefore = true;
    }

    renderer->RemoveActor(axes);
}

void DGVLatticeVTK::addActorToLattice(vtkActor *actorToAdd)
{
    renderer->AddActor(actorToAdd);
    //renderer->Update()
    renderWin->Render();
}

void DGVLatticeVTK::generateLattice()
{
    int totalPoints = 1;

    if(!generatedBefore)
    {
        sphere = vtkSphereSource::New();
        mapper = vtkPolyDataMapper::New();
        renderer = vtkRenderer::New();
        renderWin = vtkRenderWindow::New();
        generatedBefore = true;
    }

    sphere->SetRadius(defaultRadius);
    sphere->SetCenter(0,0,0);
    sphere->Update();

    mapper->SetInput(sphere->GetOutput());
    //mapper->SetScalarRange(0, radius);
    mapper->Update();

    for(int j = 0; j < dimension; j ++)
        totalPoints *= latticeSize(j);

    int count = 0;
    vtkTransform *transform;
    latticePoints.resizeAndPreserve(totalPoints);
    if(dimension == 1)
    {
        for(int j = 0; j < latticeSize(0); j ++)
        {
            transform = vtkTransform::New();
            latticePoints(count) = vtkActor::New();
            latticePoints(count)->SetMapper(mapper);
            transform->SetMatrix(latticePoints(count)->GetMatrix());
            transform->Translate(latticeParameters(0)*j,0,0);
            transform->Update();
            latticePoints(count)->PokeMatrix(transform->GetMatrix());
            latticePoints(count)->GetProperty()->SetColor(property->GetColor()[0],property->GetColor()[1],property->GetColor()[2]);
            renderer->AddActor(latticePoints(count));
            transform->Delete();
            count ++;
        }
    }
    else if(dimension == 2)
    {
        for(int j = 0; j < latticeSize(0); j ++)
        {
            for(int k = 0; k < latticeSize(1); k ++)
            {
                transform = vtkTransform::New();
                latticePoints(count) = vtkActor::New();
                latticePoints(count)->SetMapper(mapper);
                transform->SetMatrix(latticePoints(count)->GetMatrix());
                transform->Translate(latticeParameters(0)*j,latticeParameters(1)*k,0);
                transform->Update();
                latticePoints(count)->PokeMatrix(transform->GetMatrix());
                latticePoints(count)->GetProperty()->SetColor(property->GetColor()[0],property->GetColor()[1],property->GetColor()[2]);
                renderer->AddActor(latticePoints(count));
                transform->Delete();
                count ++;
            }
        }
    }
    else if(dimension == 3)
    {
        for(int j = 0; j < latticeSize(0); j ++)
        {
            for(int k = 0; k < latticeSize(1); k ++)
            {
                for(int l = 0; l < latticeSize(2); l ++)
                {
                    transform = vtkTransform::New();
                    latticePoints(count) = vtkActor::New();
                    latticePoints(count)->SetMapper(mapper);
                    transform->SetMatrix(latticePoints(count)->GetMatrix());
                    transform->Translate(latticeParameters(0)*j,latticeParameters(1)*k,latticeParameters(2)*l);
                    transform->Update();
                    latticePoints(count)->PokeMatrix(transform->GetMatrix());
                    latticePoints(count)->GetProperty()->SetColor(property->GetColor()[0],property->GetColor()[1],property->GetColor()[2]);
                    renderer->AddActor(latticePoints(count));
                    transform->Delete();
                    count ++;
                }
            }
        }
    }
    else
    {
        cerr << "ERROR: Lattice Dimension Not Supported!" << endl;
        return;
    }

    renderer->ResetCamera();
    renderer->SetBackground(0.1, 0.2, 0.4);
    renderer->ResetCameraClippingRange();
    renderWin->AddRenderer(renderer);
    SetRenderWindow(renderWin);
    renderWin->Render();
}

QString DGVLatticeVTK::strippedName()
{
    return QFileInfo(name).fileName();
}

QString DGVLatticeVTK::strippedNamePrefix()
{
    return "Lattice: " + QFileInfo(name).fileName();
}
